import numpy as np
import operator
import os


def createDataSet():
    group = np.array([[1.0, 1.1], [1.0, 1.0], [0, 0], [0, 0.1]])
    labels = ['A', 'A', 'B', 'B']
    return group, labels


def file2matrix(fileNname):
    loveDict = {'largeDoses':3, 'smallDoses':2, 'didntLike':1}
    fr = open(fileNname)
    # Read all lines, and return it's content as a list 
    # containing all lines
    arrOfLines = fr.readlines()
    numOfLines = len(arrOfLines)

    fr.close()
    # Build a num_lines-row，3-column matrix filling with 0
    retMat= np.zeros((numOfLines, 3))
    classLabelVec = []
    index = 0
    for line in arrOfLines:
        line = line.strip()
        listFromLine = line.split('\t')
        # Matrix's assignment: a 3-element list as a value
        # is assigned to one row of matrix.
        retMat[index, :] = listFromLine[0:3]
        if(listFromLine[-1].isdigit()):
            classLabelVec.append(int(listFromLine[-1]))
        else:
            classLabelVec.append(loveDict.get(listFromLine[-1]))
        index += 1

    return retMat, classLabelVec 


'''
        inX:       要分类的数据()
        dataSet:   已知标签的样本数据集
        lables:     data_set对应的标签
        k:          最近邻居的数目
'''
def classify0(inX, dataSet, labels, k):
    # dataSet：np的矩阵，返回dataSet的维度，类型是元组
    dataSetSize = np.shape(dataSet)[0]
    # 将inX构建成一个跟dataSet具有相同维度的矩阵，并与dataSet相减
    diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet
    # 平方
    sqDiffMat = diffMat ** 2;
    # 求和
    sqDistances = sqDiffMat.sum(axis=1)
    # 
    distances = sqDistances ** 0.5
    # 将distances从小到大排序，返回对应的索引值
    sortedDistIndicies = distances.argsort()

    classCnt = {}

    for i in range(k):
        voteLabels = labels[sortedDistIndicies[i]]
        # 获取字典中键voteLablels对应的值，如果此键不在字典中，则值为0
        classCnt[voteLabels] = classCnt.get(voteLabels, 0) + 1
    # dict.items()返回键值对的元组列表，key指定按照“值”来排序
    sortedClass = sorted(classCnt.items(), key=operator.itemgetter(1), reverse=True)

    return sortedClass[0][0]

def autoNorm(dataSet):
    # numpy.ndarray.min():return the minimal val along a given axis
    minVals = dataSet.min(0)
    maxVals = dataSet.max(0)
    ranges = maxVals - minVals
    normDataSet = np.zeros(np.shape(dataSet))

    m = np.shape(dataSet)[0]
    normDataSet = dataSet - np.tile(minVals, (m, 1))
    normDataSet = normDataSet/np.tile(ranges, (m, 1))

    return normDataSet, ranges, minVals

def img2vector(fineName):
    retVec = np.zeros((1, 1024))
    fr = open(fineName)
    for i in range(32):
        lineStr = fr.readline()
        for j in range(32):
            retVec[0, 32*i+j] = int(lineStr[j])


    fr.close()
    return retVec

def datingClassTest():
    hoRatio = 0.70
    datingDataMat, datingLabels = file2matrix('datingTestSet2.txt')
    normMat, ranges, minVals = autoNorm(datingDataMat)
    m = np.shape(normMat)[0]
    numTestVec = int(m * hoRatio)

    errCnt = 0

    for i in range(numTestVec):
        classRet = classify0(normMat[i,:], normMat[numTestVec:m,:], datingLabels[numTestVec:m], 3)

        # print("the classifier came back with: %d, the real answer is: %d" %(classRet, datingLabels[i]))

        if(classRet != datingLabels[i]):
            errCnt += 1.0

    print("the total error rate is: %f" %(errCnt / float(numTestVec)))
    print(errCnt)

def handWritingClassTest():
    hwLabels = []
    trainingFileList = os.listdir('trainingDigits')

    m = len(trainingFileList)
    trainingMat = np.zeros((m,1024))

    for i in range(m):
        fileNameStr = trainingFileList[i]
        fileStr = fileNameStr.split('.')[0]
        classNum = int(fileStr.split('_')[0])
        hwLabels.append(classNum)
        trainingMat[i,:] = img2vector('.\\trainingDigits\\%s' % fileNameStr)
    
    testFileList = os.listdir('testDigits')
    errCnt = 0.0
    mTest = len(testFileList)

    for i in range(mTest):
        fileNameStr = testFileList[i]
        fileStr = fileNameStr.split('.')[0]
        classNum = int(fileStr.split('_')[0])
        vecUnderTest = img2vector('.\\testDigits\\%s' % fileNameStr)
        classRet = classify0(vecUnderTest, trainingMat, hwLabels, 3)
        print("the classifier came back with: %d, the real answer is: %d" % (classRet, classNum))
        if(classRet != classNum):
            errCnt += 1.0

    print("the total num of errors is :%d" % errCnt)
    print("the total error rate is: %f" % (errCnt/float(mTest)))

            


